package site.zhouinfo.server;

import site.zhouinfo.server.ChangeScanner.AppChangeHandler;

import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.URL;

public abstract class AbstarctServer implements Server, AppChangeHandler {
    private static final Long DEFAULT_SCAN_INTERVAL_SECONDS = 5L;
    private static final int DEFAULT_PORT = 80;
    private static final String DEFAULT_WEBAPP_DIR = "src/main/webapp";
    private static final String DEFAULT_CONTEXT = "/";
    private String webAppDir;
    private Integer port;
    private Long scanIntervalSeconds;
    private boolean running;
    private String context;
    private ExcudeScanResolver excudeScanResolver = new ExcudeScanResolver();
    /**
     * 重启触发文件<br/>
     * 该文件设置后，仅有该文件被修改时才会热加载
     */
    private File restartTriggerFile;

    @Override
    public final void start() {

        if (isRunning()) {
            throw new IllegalStateException("服务器已经启动,不能重复开启");
        }

        if (getPort() < 0 || getPort() > 65536)
            throw new IllegalArgumentException("服务器端口没有指定或者不合法,合法端口：port >= 0 && port <= 65536");

        checkPort();
        doStart();
        //如果设置了修改检测间隔时间，则开始检测修改
        if (getScanIntervalSeconds() > 0) {
            new ChangeScanner()
                    .setScanIntervalSeconds(getScanIntervalSeconds())    //检测间隔时间
                    .setOnchange(this)    //修改事件监听者
                    .setExcudeScanResolver(excudeScanResolver) //排除检测解析器
                    .setTriggerFile(restartTriggerFile)    //热启动触发文件
                    .start();
        }
        System.out.println("服务器启动成功:");
        System.out.println("\t 端口：" + getPort());
        System.out.println("\t Web根目录：" + getWebAppDir());
        System.out.println("\t 修改扫描间隔：" + getScanIntervalSeconds());
        setRunning(true);
        serverJoin();
    }

    protected abstract void serverJoin();

    @Override
    public void stop() {
        if (!isRunning()) {
            throw new IllegalStateException("服务器没有启动");
        }
        doStop();
        setRunning(false);
    }

    protected abstract void doStart();

    protected abstract void doStop();

    /**
     * 检测端口是否被占用
     *
     * @create_date 2016年7月14日
     */
    private void checkPort() {
        ServerSocket ss = null;
        DatagramSocket ds = null;
        try {
            ss = new ServerSocket(getPort());
            ss.setReuseAddress(true);
            ds = new DatagramSocket(getPort());
            ds.setReuseAddress(true);
        } catch (Exception e) {
            throw new IllegalStateException("服务器启动失败：端口" + getPort() + "已占用");
        } finally {
            if (ds != null) {
                ds.close();
            }

            if (ss != null) {
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public Server port(int port) {
        this.port = port;
        return this;
    }

    public Server scanIntervalSeconds(Long scanIntervalSeconds) {
        this.scanIntervalSeconds = scanIntervalSeconds;
        return this;
    }

    public String getWebAppDir() {
        if (webAppDir == null) {
            webAppDir = DEFAULT_WEBAPP_DIR;
        }
        return webAppDir;
    }

    public Server webAppDir(String webAppDir) {
        this.webAppDir = webAppDir;
        return this;
    }

    public int getPort() {
        if (port == null) {
            port = DEFAULT_PORT;
        }
        return port;
    }

    public long getScanIntervalSeconds() {
        if (scanIntervalSeconds == null) {
            scanIntervalSeconds = DEFAULT_SCAN_INTERVAL_SECONDS;
        }
        return scanIntervalSeconds;
    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    public String getContext() {
        if (context == null) {
            context = DEFAULT_CONTEXT;
        }
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }

    @Override
    public Server addExcludeScanPath(String pattern) {
        excudeScanResolver.addExcludePath(pattern);
        return this;
    }


    public ExcudeScanResolver getExcudeScanResolver() {
        return excudeScanResolver;
    }

    public Server setExcudeScanResolver(ExcudeScanResolver excudeScanResolver) {
        this.excudeScanResolver = excudeScanResolver;
        return this;
    }

    @Override
    public Server triggerFile(String path) {
        if (!path.startsWith("/")) {
            throw new IllegalArgumentException("triggerFile路径必须以‘/’开头，表明以项目跟路径开始");
        }
        File file = new File(getRootClassFile().getAbsolutePath() + path);
        if (!file.exists() || !file.isFile()) {
            throw new IllegalArgumentException("triggerFile设置失败:没找到该文件");
        }
        this.restartTriggerFile = file;
        return this;
    }

    static File getRootClassFile() {
        URL url = ChangeScanner.class.getClassLoader().getResource("");
        try {
            return new File(url.toURI().getPath());
        } catch (Exception e) {
            return new File(url.getPath());
        }
    }
}
